package xyz.lwm.lazycat.beans;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import xyz.lwm.lazycat.annotation.Configuration;
import xyz.lwm.lazycat.utility.StrPool;
import xyz.lwm.lazycat.utility.StringUtil;
import xyz.lwm.lazycat.utility.reflect.ClassUtil;
import xyz.lwm.lazycat.utility.resource.Resource;
import xyz.lwm.lazycat.utility.scan.ScanUtil;
import xyz.lwm.lazycat.utility.scan.Scanner;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import static xyz.lwm.lazycat.utility.resource.Location.CLASSPATH;

/**
 * load bean design by spi file
 *
 * @author lwm
 */
@Slf4j
@AllArgsConstructor
class BeanDesignSpiLoader implements BeanDesignRegistryCustomizer {

    // 实例注册 spi 约定文件
    private static final String BEANS_PATH = "META-INF/lazycat/beans";

    private final BeanDesignRegistryCustomizers registryCustomizers;

    @Override
    public void customize(BeanDesignRegistry beanRegistry) {
        ScanUtil.scanResources(CLASSPATH.toPattern(BEANS_PATH))
                .stream().filter(Resource::exists)
                .filter(r -> !r.isDir())
                .forEach(r -> load(r, beanRegistry));
    }

    private void load(Resource resource, BeanDesignRegistry beanRegistry) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
            reader.lines().filter(StringUtil::isNotBlank).map(String::trim)
                    // exclude comment
                    .filter(s -> !s.startsWith(StrPool.STR_HASH))
                    .forEach(s -> registerByClassName(s, beanRegistry));
        } catch (IOException e) {
            log.info("load beans.properties error, path: {}", resource.getPath(), e);
        }
    }

    private void registerByClassName(String className, BeanDesignRegistry beanRegistry) {
        try {
            if (className.endsWith(Scanner.CLASS_SUFFIX)) {
                className = className.substring(0, className.length() - Scanner.CLASS_SUFFIX.length());
            }
            Class<?> clz = ClassUtil.forName(className, ClassUtil.getDefaultClassLoader());
            BeanDesignUtil.buildDesign(clz)
                    .also(beanRegistry::register)
                    .filter(design ->
                            design.getAnnotations().hasAnnotation(Configuration.class))
                    .also(design ->
                            registryCustomizers.add(new BeanDesignConfigurationLoader(design, registryCustomizers)));
        } catch (Exception e) {
            log.error("load service impl error, impl: {}", className, e);
        }
    }

}
